﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ClusHal
{
    class ClusHal
    {
        private List<Action> actions;
        double[,] distances;
        List<Cluster> clusters;
        List<Approximation> approximations;

        public ClusHal(string filename)
        {
            actions = new List<Action>();
            StreamReader sr = new StreamReader(filename);
            string line = sr.ReadLine();
            Action.LoadPreferences(line);
            Action.PrintPreferences();
            line = sr.ReadLine();
            while (line != null)
            {
                if (Action.preferences[0] == 'l')
                {
                    Action a = new Action(line, false);
                    actions.Add(a);
                }
                else
                {
                    Action a = new Action(line, true);
                    actions.Add(a);
                    a.Label = "A" + actions.Count.ToString();
                }
                line = sr.ReadLine();
            }
            sr.Close();
        }

        public void SaveData(string filename)
        {
            StreamWriter sw = new StreamWriter(filename);

            for (int i=0;i<Action.preferences.Length;i++)
            {
                sw.Write(Action.preferences[i].ToString());
                if (i < Action.preferences.Length - 1)
                    sw.Write("\t");
            }
            sw.WriteLine();

            foreach (Action a in actions)
            {
                for (int i = 0; i < a.Size; i++)
                {
                    sw.Write(a[i].ToString());
                    if (i < a.Size - 1)
                        sw.WriteLine("\t");
                }
                sw.WriteLine();
            }

            sw.Close();
        }

        public void CalculateDistanceMatrix()
        {
            distances = new double[actions.Count, actions.Count];
            IDistanceMeasure dist = new EuclideanDistance();
            for (int i = 0; i < actions.Count; i++)
                for (int j = 0; j < actions.Count; j++)
                    distances[i, j] = dist.Distance(actions[i], actions[j]);
        }

        public void ExportDistances(string filename)
        {
            StreamWriter sw = new StreamWriter(filename);

            sw.Write("#\t");
            for (int i = 0; i < actions.Count; i++)
            {
                sw.Write((i + 1).ToString() + "\t");
            }
            sw.WriteLine();
            for (int i = 0; i < actions.Count; i++)
            {
                sw.Write((i + 1).ToString() + "\t");
                for (int j = 0; j < actions.Count; j++)
                {
                    sw.Write(distances[i,j] + "\t");
                }
                sw.WriteLine();
            }
            sw.Close();
        }

        public void FindClusters()
        {
            //double[] weights = { 0.02, 0.03, 0.05, 0.1, 0.1, 0.2, 0.2, 0.3 };//{0.3,0.2,0.2,0.1,0.1,0.05,0.03,0.02};
            //OWADistance.weights = weights;
            kMeans km = new kMeans(3, 1000, new EuclideanDistance());
            clusters = km.Clusterize(actions);
        }

        public void PrintClusters()
        {
            Console.WriteLine("Clusters:");
            foreach (Cluster list in clusters)
            {
                Console.WriteLine(list.ToString());
            }
            Console.WriteLine("============================================");    
        }

        public void ArrangeClusters()
        {
            int size = clusters.Count;
            int[,] scoresP = new int[size, size];
            int[,] scoresN = new int[size, size];
            for (int i = 0; i < size; i++)
            {
                for (int j = 0; j < size; j++)
                {
                    if (i == j)
                    {
                        scoresP[i, j] = 0;
                        scoresN[i, j] = 0;
                    }
                    else
                    {
                        scoresP[i, j] = clusters[i].Dominates(clusters[j]);
                        scoresN[i, j] = clusters[j].Dominates(clusters[i]);
                    }
                }
            }

            List<double> sc = new List<double>();
            double[] scd = new double[size];
            for (int i = 0; i < size; i++)
            {
                double sum = 0;
                for (int j = 0; j < size; j++)
                    sum += scoresP[i, j]-scoresN[i, j];
                sum=sum / (double)clusters[i].Actions.Count;
                //clusters[i].DecisionClass = sum;
                scd[i] = sum;
                sc.Add(sum);
            }
            sc.Sort();
            for (int j=0;j<size;j++)
            {
                for (int i = 0; i < sc.Count; i++)
                {
                    if (scd[j] == sc[i])
                    {
                        clusters[j].DecisionClass = i + 1;
                        break;
                    }
                }
            }
            clusters.Sort();
            Console.WriteLine("Cluster scores");
            for (int i = 0; i < size; i++)
            {
                int ts = 0;
                for (int j = 0; j < size; j++)
                {
                    ts += scoresP[i, j]-scoresN[i,j];
                    Console.Write(scoresP[i, j].ToString() + "\t");
                }
                Console.Write(ts.ToString());
                Console.WriteLine();
            }
            Console.WriteLine("-----------------");
            for (int i = 0; i < size; i++)
            {
                int ts = 0;
                for (int j = 0; j < size; j++)
                {
                    ts += scoresP[i, j] - scoresN[i, j];
                    Console.Write(scoresN[i, j].ToString() + "\t");
                }
                Console.Write(ts.ToString());
                Console.WriteLine();
            }
            Console.WriteLine("============================================");    
        }

        public void DominanceRelation()
        {
            foreach (Action a in actions)
            {
                a.CalculateSets(actions);
            }
            Console.WriteLine("Dominance relation");
            StringBuilder sb = null;
            foreach (Action a in actions)
            {
                sb = new StringBuilder();
                Console.WriteLine(a.ToString());
                Console.Write("D+:");
                foreach (Action b in a.Dominates)
                { 
                    sb.Append(b.ToString());
                    sb.Append(",");
                }
                Console.WriteLine(" ("+a.Dominates.Count.ToString()+"): "+sb.ToString());

                sb = new StringBuilder();
                Console.Write("D-:");
                foreach (Action b in a.Dominated)
                {
                    sb.Append(b.ToString());
                    sb.Append(",");
                }
                Console.WriteLine(" (" + a.Dominated.Count.ToString() + "): " + sb.ToString());
            }
            Console.WriteLine("============================================");    
        }

        public void Approximations()
        {
            approximations = new List<Approximation>();
            for (int i = 0; i < clusters.Count; i++)
            {
                Approximation a = new Approximation(actions, clusters[i].DecisionClass, ERelation.AtLeast);
                approximations.Add(a);
            }
            for (int i = clusters.Count - 1; i >= 0; i--)
            {
                Approximation a = new Approximation(actions, clusters[i].DecisionClass, ERelation.AtMost);
                approximations.Add(a);
            }

            Console.WriteLine("Approximations");
            foreach (Approximation a in approximations)
                Console.WriteLine(a.ToString());

            double quality = Quality();
            Console.WriteLine("Quality = " + quality.ToString());

            Console.WriteLine("============================================");    

        }

        private double Quality()
        {
            Set<Action> u = new Set<Action>();
            foreach (Action a in actions)
            {
                u.Add(a);
            }

            foreach (Approximation a in approximations)
            {
                foreach (Action b in a.Boundary)
                {
                    u.Remove(b);
                }
            }
            double quality = (double)u.Count / (double)actions.Count;
            return quality;
        }

        public void SaveState(string filename)
        {
            StreamWriter sw = new StreamWriter(filename);
            sw.WriteLine("ClusHal temp state {"+actions.Count.ToString()+";"+actions[0].Size.ToString()+";"+clusters.Count.ToString()+"}");
            foreach (Action a in actions)
            {
                a.SaveToFile(sw);
            }
            sw.Close();
        }

        public void LoadState(string filename)
        {
            StreamReader sr = new StreamReader(filename);
            string line = sr.ReadLine();
            int i = line.LastIndexOf(";");
            line=line.Substring(i+1, line.Length - i-2);
            i = int.Parse(line);
            clusters = new List<Cluster>();
            for (int j = 0; j < i; j++)
            {
                Cluster c = new Cluster();
                c.DecisionClass = j + 1;
                clusters.Add(c);
            }
            line=sr.ReadLine();
            actions = new List<Action>();
            while (line != null)
            {
                Action a = new Action(line, false);
                i = line.LastIndexOf("\t");
                i = int.Parse(line.Substring(i))-1;
                actions.Add(a);
                clusters[i].Actions.Add(a);
                a.Cluster = clusters[i];
                line = sr.ReadLine();
            }
            sr.Close();
            PrintClusters();
            DominanceRelation();
            Approximations();
        }

        public void ResolveConflicts()
        {
            int c = 0;
            while (Quality() < 1 && c < 10)
            {
                c++;
                Console.WriteLine("Resolution " + c.ToString() + ":");
                for (int i = 0; i < approximations.Count; i++)
                {
                    Approximation a = approximations[i];
                    foreach (Action b in a.Boundary)
                    {
                        //przenies nizej
                        Console.WriteLine("Conflicting action: " + b.ToString());
                        int dc = b.Cluster.DecisionClass;
                        if (a.Direction == ERelation.AtLeast)
                        {
                            dc++;
                        }
                        else
                        {
                            dc--;
                        }
                        if (dc > 0 && dc <= clusters.Count)
                        {
                            b.Cluster.Actions.Remove(b);
                            clusters[dc-1].Actions.Add(b);
                            b.Cluster = clusters[dc-1];
                        }
                        else
                            Console.WriteLine("Cannont move action " + b.ToString() + " to class " + dc.ToString());
                    }
                    break;
                }
                PrintClusters();
                Approximations();
                
            }
        }
    }
}
